package com.hxk.juc.thread.volatileLearn;

import java.util.concurrent.atomic.AtomicInteger;

/***
 * volatile是java轻量级的同步机制
 *  volatile的3大特性：
 *      1、保证可见性
 *      2、不保证原子性
 *      3、禁止指令重排
 *  本case主要是对volatile【不保证原子性】的特性演示
 *      使用线程20个线程，并且每个线程中对num加1000次，在正常情况下执行结束后num的值应该是20000，但实际执行完值不是20000，
 *      这就是volatile的不保证原子性的特性，但这又不符合JMM的原子性的特性，所以说volatile是轻量级的
 *  按照JMM（java内存模型）的要求，必须保证数据的原子性，那怎么处理里？？
 *      1、可以在MyData类中的incr方法上加synchronized同步关键字来解决，但锁的粒度太大(影响系统性能)，也有点大材小用
 *      2、使用juc中的原子类（java.util.concurrent.AtomicInteger）来解决，目前最好的处理方案【推荐】
 */
public class VolatileCase2 {

    public static void main(String[] args) {

        MyData2 myData2 = new MyData2();

        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                    myData2.incr();
                    myData2.incrAtomicInteger();
                }
            },String.valueOf(i)).start();
        }

        /**
         * 为什么Thread.activeCount() > 2，因为java运行后，在后台运行java的垃圾回收gc线程和main线程
         *  所以当Thread.activeCount() > 2时，证明上面的20个线程还没有执行完毕，所以让main线程让出执行权给上面的20个线程，
         *  让他们执行完毕之后，再让程序往下执行
         */
        while (Thread.activeCount() > 2){
            Thread.yield();//让主线程（main线程）让出执行权给其他线程
        }
        System.out.println(Thread.currentThread().getName()+"volatile + int 值："+myData2.num);
        System.out.println(Thread.currentThread().getName()+"AtomicInteger 值："+myData2.atomicInteger);
    }
}

class MyData2{

    volatile int num = 0;

    //JUC 的原子整型类
    AtomicInteger atomicInteger = new AtomicInteger();

    public void incr(){
        num++;
    }

    public void incrAtomicInteger(){
        atomicInteger.getAndIncrement();
    }

}
